﻿Ext.CompositeElement = function (els, root) {

    this.elements = [];
    this.add(els, root);
    this.el = new Ext.Element.Flyweight();
};

Ext.CompositeElement.prototype = {
    isComposite: true,


    getElement: function (el) {

        var e = this.el;
        e.dom = el;
        e.id = el.id;
        return e;
    },


    transformElement: function (el) {
        return Ext.getDom(el);
    },

    //Returns the number of elements in this Composite.
    getCount: function () {
        return this.elements.length;
    },

    //Adds elements to this Composite object.
    //els : Mixed  Either an Array of DOM elements to add, or another Composite object who's elements should be added.
    //Returns: CompositeElement  This Composite object.
    add: function (els, root) {
        var me = this,
            elements = me.elements;
        if (!els) {
            return this;
        }
        if (typeof els == 'string') {
            els = Ext.Element.selectorFunction(els, root);
        }
        else if (els.isComposite) {
            els = els.elements;
        }
        else if (!Ext.isIterable(els)) {
            els = [els];
        }

        for (var i = 0, len = els.length; i < len; ++i) {
            elements.push(me.transformElement(els[i]));
        }

        return me;
    },
    //让elements数组中的所有DOM对应的Element对象执行fn方法，args为fn的参数
    invoke: function (fn, args) {
        var me = this,
            els = me.elements,
            len = els.length,
            e,
            i;

        for (i = 0; i < len; i++) {
            e = els[i];
            if (e) {
                Ext.Element.prototype[fn].apply(me.getElement(e), args);
            }
        }
        return me;
    },
    //Returns a flyweight Element of the dom element object at the specified index
    item: function (index) {
        var me = this,
            el = me.elements[index],
            out = null;

        if (el) {
            out = me.getElement(el);
        }
        return out;
    },


    addListener: function (eventName, handler, scope, opt) {
        var els = this.elements,
            len = els.length,
            i, e;

        for (i = 0; i < len; i++) {
            e = els[i];
            if (e) {
                Ext.EventManager.on(e, eventName, handler, scope || e, opt);
            }
        }
        return this;
    },

    //Calls the passed function for each element in this composite.
    each: function (fn, scope) {
        var me = this,
            els = me.elements,
            len = els.length,
            i, e;

        for (i = 0; i < len; i++) {
            e = els[i];
            if (e) {
                e = this.getElement(e);
                if (fn.call(scope || e, e, me, i)) {
                    break;
                }
            }
        }
        return me;
    },

    //Clears this Composite and adds the elements passed.
    fill: function (els) {
        var me = this;
        me.elements = [];
        me.add(els);
        return me;
    },

    //Filters this composite to only elements that match the passed selector.
    filter: function (selector) {
        var els = [],
            me = this,
            elements = me.elements,
            fn = Ext.isFunction(selector) ? selector
                : function (el) {
                    return el.is(selector);
                };

        me.each(function (el, self, i) {
            if (fn(el, i) !== false) {
                els[els.length] = me.transformElement(el);
            }
        });
        me.elements = els;
        return me;
    },

    //Returns the first Element
    first: function () {
        return this.item(0);
    },

    //Returns the last Element
    last: function () {
        return this.item(this.getCount() - 1);
    },

    //Returns true if this composite contains the passed element
    contains: function (el) {
        return this.indexOf(el) != -1;
    },

    //Find the index of the passed element within the composite collection.
    indexOf: function (el) {
        return this.elements.indexOf(this.transformElement(el));
    },

    //Removes all elements.
    clear: function () {
        this.elements = [];
    }
};

Ext.CompositeElement.prototype.on = Ext.CompositeElement.prototype.addListener;

(function () {
    var fnName,
    ElProto = Ext.Element.prototype,
    CelProto = Ext.CompositeElement.prototype;

    for (fnName in ElProto) {//把Ext.Element函数都复制到Ext.CompositeElement原型上
        if (Ext.isFunction(ElProto[fnName])) {
            (function (fnName) {//把Ext.Element函数包裹成一个新的函数
                CelProto[fnName] = CelProto[fnName] || 
                //该函数代理了所有的Ext.Element函数
                function () {
                    return this.invoke(fnName, arguments);
                };
            }).call(CelProto, fnName);

        }
    }
})();

Ext.CompositeElementLite = Ext.CompositeElement;


Ext.apply(Ext.CompositeElementLite.prototype, {
    addElements : function(els, root){
        if(!els){
            return this;
        }
        if(typeof els == "string"){
            els = Ext.Element.selectorFunction(els, root);
        }
        var yels = this.elements;
        Ext.each(els, function(e) {
            yels.push(Ext.get(e));
        });
        return this;
    },

    //Removes the specified element(s).
    //el : Mixed  The id of an element, the Element itself, the index of the element in this composite or an array of any of those.
    //removeDom : Boolean  (optional) True to also remove the element from the document
    //Returns: CompositeElement  this
    removeElement : function(keys, removeDom){
        var me = this,
            els = this.elements,
            el;
        Ext.each(keys, function(val){
            if ((el = (els[val] || els[val = me.indexOf(val)]))) {
                if(removeDom){
                    if(el.dom){
                        el.remove();
                    }else{
                        Ext.removeNode(el);
                    }
                }
                els.splice(val, 1);
            }
        });
        return this;
    },

    //Replaces the specified element with the passed element.
    //el : Mixed  The id of an element, the Element itself, the index of the element in this composite to replace.
    //replacement : Mixed  The id of an element or the Element itself.
    //domReplace : Boolean  (Optional) True to remove and replace the element in the document too.
    //Returns: CompositeElement  this
    replaceElement : function(el, replacement, domReplace){
        var index = !isNaN(el) ? el : this.indexOf(el),
            d;
        if(index > -1){
            replacement = Ext.getDom(replacement);
            if(domReplace){
                d = this.elements[index];
                d.parentNode.insertBefore(replacement, d);
                Ext.removeNode(d);
            }
            this.elements.splice(index, 1, replacement);
        }
        return this;
    }
});